home *** CD-ROM | disk | FTP | other *** search
/ Compendium Deluxe 1 / LSD Compendium Deluxe 1.iso / a / disk / misc / string11.lha / clop.c next >
Encoding:
C/C++ Source or Header  |  1990-12-30  |  7.4 KB  |  377 lines

  1. /*
  2.  *  FILE
  3.  *    clop.c
  4.  *
  5.  *  DESCRIPTION
  6.  *    Command Line Option Parser
  7.  *
  8.  *  AUTHOR
  9.  *    Anders 'ALi' Lindgren
  10.  *    Mälarblick 8
  11.  *    S-161 51 Bromma
  12.  *    Sweden
  13.  *
  14.  *  TODO
  15.  *    float (Conditional compilation)
  16.  *    New infirmation passing system. (Rename clop_list & clop_array).
  17.  *    move getargs to its own file.
  18.  *    En global Escape-flagga?
  19.  *    Bättre ALiLib-interfacing. (Flytta Clop.h, stdlib.h etc.)
  20.  *    En clop-error-meddelande rutin. Den skall returnera en pekare till
  21.  *     en (statisk) sträng som innehåller ett clop-felmeddelande. Rutinen
  22.  *     skall vara fristående så att man slipper strängarna om man inte
  23.  *     vill ha dom.
  24.  */
  25.  
  26. #include <proto/exec.h>
  27.  
  28. #include "stdlib.h"
  29. #include "string.h"
  30.  
  31. #include "clop.h"
  32.  
  33. #ifndef NULL
  34. #define NULL 0L
  35. #endif
  36.  
  37.  
  38. /*
  39.  *  DEFINITION
  40.  *    ESCAPE
  41.  *
  42.  *  DESCRIPTION
  43.  *    Special character escape.
  44.  *    Example: "allan\"hej" -> allan"hej
  45.  */
  46.  
  47. #define ESCAPE '\\'
  48.  
  49.  
  50. /*
  51.  *  DEFINITION
  52.  *    isdigit
  53.  *
  54.  *  DESCRIPTION
  55.  *    Check if it's a digit.
  56.  *    Why don't use the one in ctype.h? It's to long for just one test.
  57.  */
  58.  
  59. #define isdigit(x) ( ((x) >= '0') && ((x) <= '9') )
  60.  
  61.  
  62. /*
  63.  *  FORWARD REFERENCES
  64.  */
  65.  
  66. extern int _catol(unsigned char *, long, long *);
  67. extern int _getargs(unsigned char *, unsigned char * *);
  68.  
  69.  
  70. /*
  71.  *  FUNCTION
  72.  *    clop()
  73.  *
  74.  *  DESCRIPTION
  75.  *    Command Line Option Parser
  76.  *
  77.  *    This is the frontend of this module. It accepts a clop_array
  78.  *    prefilled with options, and a clop_list with nothing but
  79.  *    error information at the moment. And of course, it accepts
  80.  *    a zero-terminated line with options.
  81.  *
  82.  *  RETURNS
  83.  *    A pointer to the first argument which isn't an option, or
  84.  *    a NULL if an error occured. If there is only options on
  85.  *    the line, a pointer to the terminating NIL is returned.
  86.  */
  87.  
  88. unsigned char * __stdargs
  89. clop(unsigned char * line, struct clop_list * clop_list,
  90.                struct clop_array * clop_array)
  91. {
  92.     register int i;
  93.     register unsigned char ch;
  94.     unsigned char * start_of_args;
  95.     int k,j,m;
  96.  
  97.     i = 0;
  98.     start_of_args = line;
  99.  
  100.     while (1) {
  101.  
  102.     do {                    /* Skip blanks.        */
  103.         ch = line[i++];
  104.     } while (ch && (ch == ' '));
  105.  
  106.     start_of_args = & line[i-1];
  107.  
  108.     if (ch) {                /* If not end of line    */
  109.  
  110.         if (ch == '-') {            /* Option?        */
  111.         if (ch = line[i++]) {
  112.  
  113.             k = -1;
  114.             for (j=0 ; clop_array[j].type != CLOPT_END ; j++) {
  115.             if (clop_array[j].option == ch) {
  116.                 k=j;
  117.             }
  118.             }
  119.  
  120.             if (k != -1) {        /* Existing option    */
  121.  
  122.                 /*
  123.                  * Check if an option which only can be
  124.                  * present once is present several times.
  125.                  */
  126.             if ( (clop_array[k].inflags & CLOPIF_SINGLE) &&
  127.                  (clop_array[k].outflags & CLOPOF_PRESENT) ) {
  128.                 clop_list->error = CLOPE_MULTIPLE_SINGLE;
  129.                 clop_list->failing_opt = ch;
  130.                 return(NULL);
  131.             }
  132.  
  133.             clop_array[k].outflags |= CLOPOF_PRESENT;
  134.  
  135.             switch(clop_array[k].type) {
  136.             case CLOPT_NUMBER:
  137.             case CLOPT_UNUMBER:
  138.                 /*
  139.                  * Note that the same code is used for both
  140.                  * signed and unsigned number. This can be
  141.                  * done since value is an union, and "number"
  142.                  * and "unumber" actiually has the same
  143.                  * storage area.
  144.                  */
  145.                 if ( (m = _catol(& line[i], clop_array[k].type,
  146.                             &clop_array[k].value.number)
  147.                         ) != -1 ) {
  148.                 i += m;
  149.                 } else {
  150.                 clop_list->error = CLOPE_ILLEGAL_NUMBER;
  151.                 clop_list->failing_opt = ch;
  152.                 return(NULL);
  153.                 }
  154.                 break;
  155.  
  156.             case CLOPT_STRING:
  157.  
  158.                 /*
  159.                  * If this option was supplied several
  160.                  * times, the last one rules (and the
  161.                  * previous strings are released.)
  162.                  * Why? Well, you could supply a env:
  163.                  * default option line, and then the user
  164.                  * could replace that value with his own.
  165.                  */
  166.                 if ( clop_array[k].value.string ) {
  167.                     if ( clop_array[k].outflags & CLOPOF_ALLOCATED ) {
  168.                     free( clop_array[k].value.string );
  169.                     clop_array[k].outflags &= ~CLOPOF_ALLOCATED;
  170.                 }
  171.                 clop_array[k].value.string = NULL;
  172.                 }
  173.  
  174.                 if ( (m = _getargs(& line[i], & clop_array[k].
  175.                         value.string) ) != -1 ) {
  176.                 i += m;
  177.                 clop_array[k].outflags |= CLOPOF_ALLOCATED;
  178.                 } else {
  179.                 clop_list->error = CLOPE_ILLEGAL_STRING;
  180.                 clop_list->failing_opt = ch;
  181.                 return(NULL);
  182.                 }
  183.                 break;
  184.  
  185.             case CLOPT_BOOL:
  186.                 switch(line[i]) {
  187.                 case ' ':
  188.                 case '\0':
  189.                 clop_array[k].value.bool =
  190.                     ! clop_array[k].value.bool;
  191.                 break;
  192.  
  193.                 case '0':
  194.                 case '1':
  195.                 clop_array[k].value.bool = line[i++]-'0';
  196.  
  197.                 if ((line[i] != ' ') && (line[i] != '\0')) {
  198.                     clop_list->error = CLOPE_ILLEGAL_BOOL;
  199.                     clop_list->failing_opt = ch;
  200.                     return(NULL);
  201.                 }
  202.                 break;
  203.  
  204.                 default:
  205.                 clop_list->error = CLOPE_ILLEGAL_BOOL;
  206.                 clop_list->failing_opt = ch;
  207.                 return(NULL);
  208.                 } /* Bool switch */
  209.  
  210.                 break;
  211.  
  212.             default:
  213.                 clop_list->error = CLOPE_UNKNOWN_TYPE;
  214.                 clop_list->failing_opt = ch;
  215.             } /* switch */
  216.  
  217.             }
  218.             else {
  219.                 /* option not found */
  220.             clop_list->error = CLOPE_UNKNOWN_OPTION;
  221.             clop_list->failing_opt = ch;
  222.             return(NULL);
  223.             }
  224.  
  225.         } /* The line was ended by a "-" */
  226.         else {
  227.             return(start_of_args);
  228.         }
  229.  
  230.         } /* No more options. */
  231.         else {
  232.         return(start_of_args);
  233.         }
  234.  
  235.     } /* End of the line. Return a pointer to the last '\0' */
  236.     else {
  237.         return(start_of_args);
  238.     }
  239.     } /* while (1) */
  240. }
  241.  
  242.  
  243. /*
  244.  *  FUNCTION
  245.  *    _catol
  246.  *
  247.  *  RETURNS
  248.  *    The length of the string processed.
  249.  *
  250.  *  DESCRIPTION
  251.  *    Convert an ascii string to a long, or a unsigned long.
  252.  *
  253.  *    "type" is set to the current type (NUMBER or UNUMBER).
  254.  *
  255.  *    "value" is declared as a long, but it can also work as
  256.  *    a unsiged long.
  257.  *
  258.  *  RETURNS
  259.  *    The number of characters processed, or -1 if an error occured.
  260.  *
  261.  *  NOTE
  262.  *    No overflow chech is made, but I don't really think it's necesary.
  263.  */
  264.  
  265. static int
  266. _catol(unsigned char * string, long type, long * value)
  267. {
  268.     long number = 0;
  269.     int i = 0;
  270.     unsigned char ch;
  271.     int negative = 0;
  272.  
  273.     /* Check minus sign */
  274.     if ( (string[0] == '-') && (type == CLOPT_NUMBER) ) {
  275.     negative = 1;
  276.     i=1;
  277.     }
  278.  
  279.     ch = string[i++];
  280.     if ( isdigit(ch) ) {
  281.     
  282.     do {
  283.         number = ((number << 3) + (number << 1)) + ch - '0';
  284.         ch = string[i++];
  285.     } while ( isdigit(ch) );
  286.  
  287.     if ( (ch == '\0') || (ch == ' ') ) {
  288.  
  289.         if (negative) {
  290.         number = -number;
  291.         }
  292.  
  293.         (* value) = number;
  294.         return(i-1);
  295.     }
  296.     }
  297.     return(-1);    /* Error */
  298. }
  299.  
  300.  
  301. /*
  302.  *  FUNCTION
  303.  *    _getargs
  304.  *
  305.  *  DESCRIPTION
  306.  *    Get Argument String
  307.  *    This function gets an argument string. It can handle quotes etc.
  308.  *
  309.  *  RETURNS
  310.  *    the number of characters parsed, or -1 if an error occured.
  311.  */
  312.  
  313. int
  314. _getargs(unsigned char * str, unsigned char * * destptr)
  315. {
  316.     unsigned char tmpbuf[256];
  317.  
  318.     unsigned char ch;
  319.     long quoted = 0;
  320.     int  s = 0;    /* source index */
  321.     int  d = 0;    /* dest index */
  322.     int  quit = 0;
  323.  
  324.     while ( str[s] == ' ') {    /* Skip initial blanks    */
  325.     s++;
  326.     }
  327.  
  328.     if (str[s] == '"') {    /* String quoted?    */
  329.     quoted = 1;
  330.     s++;
  331.     }
  332.  
  333.     while(! quit) {
  334.     switch (ch = str[s++]) {
  335.     case '\0':
  336.         quit = 1;
  337.         break;
  338.  
  339.     case ' ':
  340.         if (quoted) {
  341.         tmpbuf[d++] = ' ';
  342.         }
  343.         else {
  344.         quit = 1;
  345.         }
  346.         break;
  347.  
  348.     case '"':
  349.         if (quoted) {
  350.         quit = 1;
  351.         }
  352.         else {
  353.         tmpbuf[d++] = '"';
  354.         }
  355.         break;
  356.  
  357.     case ESCAPE:
  358.         if ( (ch = str[s++]) == '\0' ) {
  359.         tmpbuf[d++] = ESCAPE;
  360.         quit = 1;
  361.         }
  362.         /* Fall through */
  363.  
  364.     default:
  365.         tmpbuf[d++] = ch;
  366.     }
  367.     } /* while */
  368.  
  369.     if ((* destptr) = (unsigned char *)malloc(d+1)) {
  370.     memcpy((void *)* destptr, (void *)tmpbuf, d);
  371.     (* destptr)[d] = '\0';
  372.     return(s);    /* Return the parsed length */
  373.     }
  374.  
  375.     return(-1);
  376. }
  377.